glymour 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in glymour.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1,5 @@
1
+ Installation
2
+
3
+ This gem requires a copy of the R interpreter and the vcd library in R.
4
+
5
+ Install the packages in required_r_packages with "R CMD INSTALL [package name]".
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/glymour.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "glymour/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "glymour"
7
+ s.version = Glymour::VERSION
8
+ s.authors = ["Brian Stanwyck"]
9
+ s.email = ["brian.stanwyck@ncf.edu"]
10
+ s.homepage = ""
11
+ s.summary = %q{A gem for supervised Bayesian net structure learning}
12
+ s.description = %q{Implements supervised Bayesian structure learning, as well as extra tools to help train a Bayesian net using ActiveRecord data}
13
+
14
+ s.add_development_dependency "rspec"
15
+ s.add_development_dependency "pry"
16
+ s.add_development_dependency "ruby-debug"
17
+
18
+ s.add_dependency 'rgl'
19
+ s.add_dependency 'sbn'
20
+ s.add_dependency 'rinruby'
21
+
22
+ s.rubyforge_project = "glymour"
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.require_paths = ["lib"]
28
+ end
@@ -0,0 +1,3 @@
1
+ module Glymour
2
+ VERSION = "0.0.1"
3
+ end
data/lib/glymour.rb ADDED
@@ -0,0 +1,241 @@
1
+ require "glymour"
2
+ require "pry"
3
+ require "rinruby"
4
+ require "rgl/adjacency"
5
+ require "rgl/topsort"
6
+ require "stats_module"
7
+
8
+ # Generates the complete graph on n vertices if n is an integer, otherwise
9
+ # the complete graph on the vertices in the enumerable given
10
+ def complete_graph(n)
11
+ set = (Integer === n) ? 1..n : n
12
+ RGL::ImplicitGraph.new do |g|
13
+ g.vertex_iterator { |b| set.each(&b) }
14
+ g.adjacent_iterator do |x, b|
15
+ set.each { |y| b.call(y) unless x == y }
16
+ end
17
+ end
18
+ end
19
+
20
+ def remove_edge(orig, e)
21
+ new_graph = RGL::ImplicitGraph.new do |g|
22
+ g.vertex_iterator { |b| orig.vertices.each(&b) }
23
+ g.adjacent_iterator do |x, b|
24
+ new_adj = orig.adjacent_vertices(x).reject { |v| e.source == v or e.target == v }
25
+ new_adj.each { |y| b.call(y) }
26
+ end
27
+ end
28
+ new_graph
29
+ end
30
+
31
+ # Takes a list of vertices and a hash of source => [targets] pairs and generates a directed graph
32
+ def make_directed(vertices, directed_edges)
33
+ g = RGL::DirectedAdjacencyGraph.new
34
+
35
+ vertices.each { |v| g.add_vertex(v) }
36
+
37
+ directed_edges.each do |source, targets|
38
+ targets.each { |target| g.add_edge(source, target) }
39
+ end
40
+
41
+ g
42
+ end
43
+
44
+ # Takes a list of vertices and a hash of source => [targets] pairs and generates an implicit (undirected) graph
45
+ def make_implicit(vertices, edges)
46
+ RGL::ImplicitGraph.new do |g|
47
+ edges.default = []
48
+ g.vertex_iterator { |b| vertices.each(&b) }
49
+ g.adjacent_iterator do |x, b|
50
+ vertices.each {|y| b.call(y) if edges[x].include? y}
51
+ end
52
+ end
53
+ end
54
+
55
+ def cartprod(*args)
56
+ result = [[]]
57
+ while [] != args
58
+ t, result = result, []
59
+ b, *args = args
60
+ t.each do |a|
61
+ b.each do |n|
62
+ result << a + [n]
63
+ end
64
+ end
65
+ end
66
+ result
67
+ end
68
+
69
+ module Glymour
70
+ # Provides graph structures and algorithms for determining edge structure of a Bayesian net
71
+ module StructureLearning
72
+ module PowerSet
73
+ # Sets an array to its "power array" (array of subarrays)
74
+ def power_set!
75
+ return [[]] if empty?
76
+ first = shift
77
+ rest = power_set!
78
+
79
+ rest + rest.map {|subset| [first] + subset }
80
+ end
81
+
82
+ def power_set
83
+ return clone.power_set!
84
+ end
85
+ end
86
+
87
+ module GraphAlgorithms
88
+ def has_edge?(e)
89
+ self.edges.include? e
90
+ end
91
+
92
+ # Returns a (unique) list of vertices adjacent to vertex a or b.
93
+ # This is denoted "Aab" in Spirtes-Glymour's paper.
94
+ def adjacent_either(a, b)
95
+ (adjacent_undirected(a) + adjacent_undirected(b)).uniq
96
+ end
97
+
98
+ def adjacent_undirected(vertex)
99
+ adjacent_sources = vertices.select { |w| adjacent_vertices(w).include?(vertex) }
100
+ adjacent_vertices(vertex) + adjacent_sources
101
+ end
102
+
103
+ # Returns an array of all vertices on undirected simple paths between s and t.
104
+ # Modified breadth-first search: keep track of current path, and when t is found, add it to paths.
105
+ # This is denoted "Uab" in Spirtes-Glymour's paper.
106
+ def verts_on_paths(current_vertex, t, current_path=[], paths=[])
107
+ if current_vertex == t
108
+ paths << current_path + [current_vertex]
109
+ else
110
+ adjacent_undirected(current_vertex).each do |v|
111
+ # Don't recur if we're repeating vertices (i.e. reject non-simple paths)
112
+ verts_on_paths(v, t, current_path + [current_vertex], paths) if current_path.count(current_vertex) == 0
113
+ end
114
+ end
115
+
116
+ paths.flatten.uniq
117
+ end
118
+
119
+ # Returns a list of _ordered_ 3-tuples (a, b, c) of vertices such that
120
+ # (a, b) are adjacent and (b,c) are adjacent, but (a,c) are not.
121
+ def non_transitive
122
+ triples = vertices.product(vertices, vertices)
123
+
124
+ adjacent_triples = triples.select do |triple|
125
+ adjacent_undirected(triple.first).include?(triple[1]) && adjacent_undirected(triple[1]).include?(triple.last)
126
+ end
127
+
128
+ adjacent_triples.reject do |triple|
129
+ (adjacent_undirected(triple.first).include? triple.last) || (triple.first == triple.last)
130
+ end
131
+ end
132
+ end
133
+
134
+ class LearningNet
135
+ include Glymour::Statistics
136
+ attr_accessor :net, :directed_edges, :n
137
+ attr_reader :p_value
138
+
139
+ def initialize(variable_container, p_value = 0.05)
140
+ @net = complete_graph(variable_container.variables).extend(GraphAlgorithms)
141
+ @directed_edges = {}
142
+ @directed_edges.default = []
143
+ @n = -1
144
+ @p_value = p_value
145
+ end
146
+
147
+ # Perform one step of the PC algorithm
148
+ def step
149
+ any_independent = false
150
+ net.edges.each do |e|
151
+ a, b = e.source, e.target
152
+ intersect = (@net.adjacent_either(a, b) & @net.verts_on_paths(a, b)).extend(PowerSet)
153
+
154
+ # Is |Aab ^ Uab| > n?
155
+ if intersect.length <= n
156
+ next
157
+ else
158
+ # Are a and b independent conditioned on any subsets of Aab ^ Uab of cardinality n+1?
159
+ valid_intersects = intersect.power_set.select {|s| s.length == n+1}.reject { |subset| subset.include?(a) || subset.include?(b) }
160
+ if valid_intersects.any? { |subset|
161
+ print "Testing independence between #{a.name} and #{b.name}, conditioning on #{(subset.any? ? subset.map(&:name).join(', ') : 'nothing') + '...'}"
162
+ print (coindependent?(p_value, a, b, *subset) ? "[+]\n" : "[-]\n")
163
+ coindependent?(p_value, a, b, *subset)
164
+ }
165
+ @net = remove_edge(net, e)
166
+ net.edges.each do |e|
167
+ puts "#{e.source.name} => #{e.target.name}"
168
+ end
169
+ any_independent = true
170
+ end
171
+ end
172
+ end
173
+ @n += 1
174
+ any_independent
175
+ end
176
+
177
+ # Perform the PC algorithm in full
178
+ def learn_structure
179
+ puts "Learning undirected net structure..."
180
+ # Perform step until every pair of adjacent variables is dependent, and
181
+ # set final_net to the _second-to-last_ state of @net
182
+ begin
183
+ puts "n = #{n}"
184
+ final_net = net
185
+ step
186
+ end while n < 1
187
+
188
+ net = final_net
189
+
190
+ direct_edges
191
+ end
192
+
193
+ # Direct remaining edges in @net as much as possible
194
+ def direct_edges
195
+ puts "Directing edges where possible..."
196
+
197
+ net.non_transitive.each do |triple|
198
+ a, b, c = *triple
199
+
200
+ intersect = (net.adjacent_either(a, c) & net.verts_on_paths(a, c)).extend(PowerSet)
201
+ if intersect.power_set.select {|s| s.include? b}.none? { |subset|
202
+ coindependent?(p_value, a, c, *subset)
203
+ }
204
+ puts "Adding directed edge #{a.name} => #{b.name}..."
205
+ @directed_edges[a] = (@directed_edges[a] << b).uniq
206
+
207
+ puts "Adding directed edge #{c.name} => #{b.name}..."
208
+ @directed_edges[c] = (@directed_edges[c] << b).uniq
209
+ end
210
+ end
211
+ end
212
+
213
+
214
+ # Gives a list of all orientations of @net compatible with @directed_edges
215
+ # (i.e., all directed acyclic graphs with edge structure given partially by @directed_edges)
216
+ def compatible_orientations
217
+ compat_list = []
218
+ edges = net.edges.extend(PowerSet)
219
+
220
+ # Every orientation of net corresponds to a subset of its edges
221
+ edges.power_set.each do |subset|
222
+ # Orient edges in subset as source => target, outside of it as target => source
223
+ # Any edges conflicting with directed_edges will be cyclic and therefore not counted
224
+ current_orientation = make_directed(net.vertices, @directed_edges)
225
+
226
+ edges.each do |e|
227
+ if subset.include? e
228
+ current_orientation.add_edge(e.source, e.target)
229
+ else
230
+ current_orientation.add_edge(e.target, e.source)
231
+ end
232
+ end
233
+
234
+ compat_list << current_orientation if current_orientation.acyclic?
235
+ end
236
+
237
+ compat_list
238
+ end
239
+ end
240
+ end
241
+ end
data/lib/scratch.rb ADDED
@@ -0,0 +1,23 @@
1
+ class WeightNet
2
+ attr_accessor :net
3
+
4
+ # Use a DAG generated by StructureLearning to create an unweighted Bayes net
5
+ def initialize(dag)
6
+ vars = {}
7
+ @net = Sbn::Net.new(title)
8
+
9
+ vertices.each do |v|
10
+ vars[v] = Sbn::Variable.new(@net, v.name.to_sym)
11
+ end
12
+
13
+ edges.each do |e|
14
+ vars[e.source].add_child(vars[e.target])
15
+ end
16
+ end
17
+ end
18
+
19
+ R.eval <<-EOF
20
+ partial_table <- t[,,#{value.join(',')}]
21
+ chisq <- chisq.test(partial_table)
22
+ s <- chisq$statistic
23
+ EOF
@@ -0,0 +1,116 @@
1
+ module Glymour
2
+ module Statistics
3
+ class VariableContainer
4
+ attr_reader :table, :number_unnamed
5
+ attr_accessor :variables
6
+
7
+ def initialize(table, variables=[])
8
+ number_unnamed = 0
9
+ @table = table
10
+ @variables = variables
11
+ @variables.each do |var|
12
+ var.variable_container = self
13
+ var.set_intervals if var.num_classes
14
+ var.name ||= "unnamed_variable#{number_unnamed += 1}"
15
+ end
16
+ end
17
+ end
18
+
19
+ class Variable
20
+ attr_accessor :intervals, :variable_container, :name, :num_classes
21
+
22
+ def initialize(name = nil, num_classes = nil, &block)
23
+ @block = Proc.new &block
24
+ @num_classes = num_classes
25
+ @intervals = num_classes && variable_container ? set_intervals : nil
26
+
27
+ # names are used as variable names in R, so make sure there's no whitespace
28
+ @name = name.gsub(/\s+/, '_') if name
29
+ end
30
+
31
+ # Apply @block to each column value, and
32
+ # return a list of evenly divided intervals [x1, x2, ..., x(n_classes)]
33
+ # So that x1 is the minimum, xn is the max
34
+ def set_intervals
35
+ vals = self.values
36
+ step = (vals.max - vals.min)/(num_classes-1).to_f
37
+ @intervals = (0..(num_classes-1)).map { |k| vals.min + k*step }
38
+ end
39
+
40
+ def value_at(row)
41
+ intervals ? location_in_interval(row) : @block.call(row)
42
+ end
43
+
44
+ # Gives an array of all variable values in table
45
+ def values
46
+ intervals ? variable_container.table.map { |row| location_in_interval(row) } : variable_container.table.map(&@block)
47
+ end
48
+
49
+ # Gives the location of a column value within a finite set of interval values (i.e. gives discrete state after classing a continuous variable)
50
+ def location_in_interval(row)
51
+ intervals.each_with_index do |x, i|
52
+ return i if @block.call(row) <= x
53
+ end
54
+
55
+ # Return -1 if value is not within intervals
56
+ -1
57
+ end
58
+ end
59
+
60
+ # Takes two or more Variables
61
+ # Returns true if first two variables are coindependent given the rest
62
+ def coindependent?(p_val, *variables)
63
+ #TODO: Raise an exception if variables have different tables?
64
+ R.echo(false)
65
+ # Push variable data into R
66
+ variables.each do |var|
67
+ # Rinruby can't handle true and false values, so use 1 and 0 resp. instead
68
+ sanitized_values = var.values.map do |value|
69
+ case value
70
+ when true then 1
71
+ when false then 0
72
+ else value
73
+ end
74
+ end
75
+
76
+ R.assign var.name, sanitized_values
77
+ end
78
+
79
+ R.eval <<-EOF
80
+ cond_data <- data.frame(#{variables.map(&:name).join(', ')})
81
+ t <-table(cond_data)
82
+ EOF
83
+
84
+ cond_vars = variables[2..(variables.length-1)]
85
+
86
+ # If no conditioning variables are given, just return the chi square test for the first two
87
+ if cond_vars.empty?
88
+ R.eval "chisq <- chisq.test(t)"
89
+ observed_p = R.pull "chisq$p.value"
90
+ return observed_p > p_val
91
+ end
92
+
93
+ cond_values = cond_vars.map { |var| (1..var.values.uniq.length).collect }
94
+
95
+ # Find the chi-squared statistic for every state of the conditioning variables and sum them
96
+ chisq_sum = 0
97
+ df = 0
98
+ cond_values.inject!(&:product).map(&:flatten)
99
+ cond_values.each do |value|
100
+ R.eval <<-EOF
101
+ partial_table <- t[,,#{value.join(',')}]
102
+ table_without_zero_columns <- partial_table[,-(which(colSums(partial_table) == 0))]
103
+ chisq <- chisq.test(table_without_zero_columns)
104
+ s <- chisq$statistic
105
+ EOF
106
+
107
+ observed_s = R.pull("s").to_f
108
+ chisq_sum += observed_s
109
+ df += R.pull("chisq$parameter").to_i
110
+ end
111
+ # Compute the p-value of the sum of statistics
112
+ observed_p = 1 - R.pull("pchisq(#{chisq_sum}, #{df})").to_f
113
+ observed_p > p_val
114
+ end
115
+ end
116
+ end
Binary file
data/spec/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
@@ -0,0 +1,62 @@
1
+ require "glymour"
2
+
3
+ class StatsDummy
4
+ include Glymour::Statistics
5
+ end
6
+
7
+ # Returns true with probability p
8
+ def prob(p)
9
+ rand < p
10
+ end
11
+
12
+ def alarm_init
13
+ @alarm_data = []
14
+ 100000.times do
15
+ earthquake = prob(0.01)
16
+ burglary = prob(0.007)
17
+
18
+ if burglary
19
+ alarm = earthquake ? prob(0.95) : prob(0.94)
20
+ else
21
+ alarm = earthquake ? prob(0.29) : prob(0.001)
22
+ end
23
+
24
+ john_calls = alarm ? prob(0.90) : prob(0.05)
25
+ mary_calls = alarm ? prob(0.70) : prob(0.01)
26
+
27
+ alarm_continuous = rand(50)
28
+
29
+ @alarm_data << { :e => earthquake, :b => burglary, :a => alarm, :j => john_calls, :m => mary_calls, :ac => alarm_continuous }
30
+ end
31
+ @e = Glymour::Statistics::Variable.new("Earthquake") { |r| r[:e] }
32
+ @b = Glymour::Statistics::Variable.new("Burglary") { |r| r[:b] }
33
+ @a = Glymour::Statistics::Variable.new("Alarm") { |r| r[:a] }
34
+ @j = Glymour::Statistics::Variable.new("John Calls") { |r| r[:j] }
35
+ @m = Glymour::Statistics::Variable.new("Mary Calls") { |r| r[:m] }
36
+
37
+ @ac = Glymour::Statistics::Variable.new("Alarm Continuous", 10) { |r| r[:ac] }
38
+
39
+ alarm_vars = [@e, @b, @a, @j, @m, @ac]
40
+
41
+ alarm_container = Glymour::Statistics::VariableContainer.new(@alarm_data, alarm_vars)
42
+ @alarm_net = Glymour::StructureLearning::LearningNet.new(alarm_container)
43
+ end
44
+
45
+ def coin_init
46
+ # Highly simplified test net
47
+ # Only edges should be @h pointing to @red and @blue
48
+ @coin_data = []
49
+ 10000.times do
50
+ h = prob(0.5)
51
+ red = h ? prob(0.2) : prob(0.7)
52
+ blue = h ? prob(0.4) : prob(0.9)
53
+ @coin_data << { :h => h, :red => red, :blue => blue }
54
+ end
55
+
56
+ @h = Glymour::Statistics::Variable.new("Heads") { |r| r[:h] }
57
+ @red = Glymour::Statistics::Variable.new("Red") { |r| r[:red] }
58
+ @blue = Glymour::Statistics::Variable.new("Blue") { |r| r[:blue] }
59
+
60
+ coin_container = Glymour::Statistics::VariableContainer.new(@coin_data, [@h, @red, @blue])
61
+ @coin_net = Glymour::StructureLearning::LearningNet.new(coin_container)
62
+ end
@@ -0,0 +1,39 @@
1
+ require 'glymour'
2
+ require 'rgl/implicit'
3
+ require 'rgl/dot'
4
+ require 'spec_helper'
5
+
6
+ describe Glymour::Statistics do
7
+ before(:all) do
8
+ R.echo(true)
9
+ Stats = StatsDummy.new
10
+
11
+ alarm_init
12
+ coin_init
13
+ end
14
+
15
+ it 'should give chi square independence data for two variables' do
16
+ Stats.coindependent?(0.05, @h, @red).should be_false
17
+ Stats.coindependent?(0.05, @e, @b).should be_true
18
+ end
19
+
20
+ it 'should give conditional independence data for several variables' do
21
+ Stats.coindependent?(0.05, @red, @blue, @h).should be_true
22
+ Stats.coindependent?(0.05, @j, @m, @a).should be_true
23
+ Stats.coindependent?(0.05, @b, @ac, @a).should be_true
24
+ end
25
+
26
+ describe Glymour::Statistics::VariableContainer
27
+ it 'should set variable name when nil' do
28
+ var = Glymour::Statistics::Variable.new {|r| r}
29
+ container = Glymour::Statistics::VariableContainer.new([], [var])
30
+ var.name.should_not be_nil
31
+ end
32
+
33
+ it 'should create unique names for variables' do
34
+ var1 = Glymour::Statistics::Variable.new {|r| r}
35
+ var2 = Glymour::Statistics::Variable.new {|r| r}
36
+ container = Glymour::Statistics::VariableContainer.new([], [var1, var2])
37
+ var1.name.should_not eq var2.name
38
+ end
39
+ end
@@ -0,0 +1,82 @@
1
+ require 'glymour'
2
+ require 'rgl/implicit'
3
+ require 'rgl/dot'
4
+ require 'spec_helper'
5
+
6
+ describe Glymour::StructureLearning do
7
+ before(:each) do
8
+ extend Glymour::StructureLearning
9
+ end
10
+
11
+ it 'should compute the power set of an array' do
12
+ ary = [1, :two, "three"].extend(Glymour::StructureLearning::PowerSet)
13
+ result = ary.power_set
14
+
15
+ [[], [1, :two], [:two, "three"], ary].each do |set|
16
+ result.should include set
17
+ end
18
+ end
19
+
20
+ describe 'Within GraphAlgorithms' do
21
+ before(:each) do
22
+ class RGL::ImplicitGraph
23
+ include Glymour::StructureLearning::GraphAlgorithms
24
+ end
25
+
26
+ # Create a graph for graph algorithm tests
27
+ # (Unfortunately we need something a little complicated for some tests)
28
+ vertices = (1..8).collect
29
+ edges = {1 => [2], 2 => [3], 3 => [1, 7], 4 => [3], 7 => [5, 8], 5 => [6], 6 => [7]}
30
+
31
+ @g = make_implicit(vertices, edges)
32
+ end
33
+
34
+ it 'should remove an edge from a graph' do
35
+ g = complete_graph(4)
36
+ orig_edge_count = g.edges.length
37
+ remove_edge(g, g.edges.first).edges.length.should_not eq orig_edge_count
38
+ end
39
+
40
+ it 'should compute the vertices on all paths between two vertices' do
41
+ path_verts = @g.verts_on_paths(4, 6)
42
+ [4, 3, 7, 5, 6].each { |v| path_verts.should include v }
43
+ end
44
+
45
+ it 'should compute non-transitive vertices of a graph' do
46
+ [[4, 3, 1], [4, 3, 7], [3, 7, 8]].each do |triple|
47
+ @g.non_transitive.should include triple
48
+ end
49
+ end
50
+
51
+ it 'should compute a complete graph on any vertex set' do
52
+ vert_set = [1, :two, "three", [5]]
53
+ complete = complete_graph vert_set
54
+
55
+ vert_set.each do |v|
56
+ complete.adjacent_vertices(v).should eq vert_set - [v]
57
+ end
58
+ end
59
+ end
60
+
61
+ describe Glymour::StructureLearning::LearningNet do
62
+ before(:all) do
63
+ alarm_init
64
+ end
65
+
66
+ it 'should perform the structure learning algorithm' do
67
+ prev_n_edges = @alarm_net.net.edges.length
68
+
69
+ @alarm_net.learn_structure
70
+
71
+ @alarm_net.net.edges.length.should be < prev_n_edges
72
+
73
+ @alarm_net.net.edges.each do |e|
74
+ puts "#{e.source.name} => #{e.target.name}"
75
+ end
76
+ end
77
+
78
+ it 'should produce orientations compatible with learn_structure output' do
79
+ orientations = @alarm_net.compatible_orientations
80
+ end
81
+ end
82
+ end
metadata ADDED
@@ -0,0 +1,166 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: glymour
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Brian Stanwyck
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-28 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :development
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: pry
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ hash: 3
43
+ segments:
44
+ - 0
45
+ version: "0"
46
+ type: :development
47
+ version_requirements: *id002
48
+ - !ruby/object:Gem::Dependency
49
+ name: ruby-debug
50
+ prerelease: false
51
+ requirement: &id003 !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ type: :development
61
+ version_requirements: *id003
62
+ - !ruby/object:Gem::Dependency
63
+ name: rgl
64
+ prerelease: false
65
+ requirement: &id004 !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ type: :runtime
75
+ version_requirements: *id004
76
+ - !ruby/object:Gem::Dependency
77
+ name: sbn
78
+ prerelease: false
79
+ requirement: &id005 !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ type: :runtime
89
+ version_requirements: *id005
90
+ - !ruby/object:Gem::Dependency
91
+ name: rinruby
92
+ prerelease: false
93
+ requirement: &id006 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ type: :runtime
103
+ version_requirements: *id006
104
+ description: Implements supervised Bayesian structure learning, as well as extra tools to help train a Bayesian net using ActiveRecord data
105
+ email:
106
+ - brian.stanwyck@ncf.edu
107
+ executables: []
108
+
109
+ extensions: []
110
+
111
+ extra_rdoc_files: []
112
+
113
+ files:
114
+ - .gitignore
115
+ - .rspec
116
+ - Gemfile
117
+ - README
118
+ - Rakefile
119
+ - glymour.gemspec
120
+ - lib/glymour.rb
121
+ - lib/glymour/version.rb
122
+ - lib/scratch.rb
123
+ - lib/stats_module.rb
124
+ - required_r_packages/colorspace_1.1-0.tgz
125
+ - required_r_packages/vcd_1.2-11.tgz
126
+ - spec/.rspec
127
+ - spec/spec_helper.rb
128
+ - spec/statistics_spec.rb
129
+ - spec/structure_learning_spec.rb
130
+ homepage: ""
131
+ licenses: []
132
+
133
+ post_install_message:
134
+ rdoc_options: []
135
+
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ hash: 3
144
+ segments:
145
+ - 0
146
+ version: "0"
147
+ required_rubygems_version: !ruby/object:Gem::Requirement
148
+ none: false
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ hash: 3
153
+ segments:
154
+ - 0
155
+ version: "0"
156
+ requirements: []
157
+
158
+ rubyforge_project: glymour
159
+ rubygems_version: 1.8.7
160
+ signing_key:
161
+ specification_version: 3
162
+ summary: A gem for supervised Bayesian net structure learning
163
+ test_files:
164
+ - spec/spec_helper.rb
165
+ - spec/statistics_spec.rb
166
+ - spec/structure_learning_spec.rb