bn4r 0.1.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/CHANGELOG +3 -0
- data/README +62 -0
- data/Rakefile +85 -0
- data/lib/bn4r/bn.rb +249 -0
- data/lib/bn4r/bn_algorithms.rb +215 -0
- data/lib/bn4r/bn_table_probabilities.rb +65 -0
- data/lib/bn4r/version.rb +9 -0
- data/lib/bn4r.rb +11 -0
- data/test/bn4r_test.rb +290 -0
- data/test/test_helper.rb +2 -0
- metadata +74 -0
data/CHANGELOG
ADDED
data/README
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
= Bayesian Networks for Ruby ( bn4r )
|
3
|
+
|
4
|
+
bn4r is a bayesian networks library on ruby that provides
|
5
|
+
the user with classes for create bayesian networks and
|
6
|
+
diverse algorithms for solve them.
|
7
|
+
|
8
|
+
Its algorithms implementation are based on: S.Russell, P.Norving, "Artificial
|
9
|
+
Intelligence, A Modern Approach", 2nd Edition.
|
10
|
+
|
11
|
+
Website:
|
12
|
+
http://bn4r.rubyforge.org
|
13
|
+
|
14
|
+
Rubyforge Project:
|
15
|
+
http://rubyforge.org/projects/bn4r
|
16
|
+
|
17
|
+
= Dependencies
|
18
|
+
|
19
|
+
* rgl-0.2.3 ( Ruby Graph Library ), http://rgl.rubyforge.org
|
20
|
+
|
21
|
+
= Design principles
|
22
|
+
|
23
|
+
The library consists on the object BayesNet thinked to be filled with
|
24
|
+
BayesNetNode, these objects are defined in bn.rb. BayesNet object is a
|
25
|
+
especialization of RGL::DirectedAdjacencyGraph ( http://rgl.rubyforge.org ).
|
26
|
+
|
27
|
+
|
28
|
+
The file bn_algorithms.rb has the implementation of the inference algorithms
|
29
|
+
that can be used to solve BayesNet structures.
|
30
|
+
|
31
|
+
Finally, a set of objects and methods are given to automaticly fill BayesNetNode
|
32
|
+
probabilities tables.
|
33
|
+
|
34
|
+
|
35
|
+
= Documentation
|
36
|
+
|
37
|
+
Documentation can be found at
|
38
|
+
http://bn4r.rubyforge.org/rdoc
|
39
|
+
or can be generated using rdoc tool under the source code with:
|
40
|
+
rdoc README lib
|
41
|
+
|
42
|
+
= Credits
|
43
|
+
|
44
|
+
Thanks to N�ria Bel ( http://www.upf.edu/pdi/iula/nuria.bel ) for her work in this project
|
45
|
+
without her it cannot be done.
|
46
|
+
|
47
|
+
Thanks to Ryan Dahl for his work in http://www.math.rochester.edu/people/grads/rld/bayesnets
|
48
|
+
that was the inspiration of the project.
|
49
|
+
|
50
|
+
Also thanks to all the ruby community.
|
51
|
+
|
52
|
+
== Copying
|
53
|
+
|
54
|
+
This work is developed by Sergio Espeja ( http://www.upf.edu/pdi/iula/sergio.espeja, sergio.espeja at gmail.com )
|
55
|
+
mainly in Institut Universitari de Ling�istica Aplicada of Universitat Pompeu Fabra ( http://www.iula.upf.es ),
|
56
|
+
and also in bee.com.es ( http://bee.com.es ).
|
57
|
+
|
58
|
+
It is free software, and may be redistributed under GPL license.
|
59
|
+
|
60
|
+
== Support
|
61
|
+
|
62
|
+
Please contact me in http://rubyforge.org/projects/bn4r.
|
data/Rakefile
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'fileutils'
|
10
|
+
include FileUtils
|
11
|
+
require File.join(File.dirname(__FILE__), 'lib', 'bn4r', 'version')
|
12
|
+
|
13
|
+
AUTHOR = "Sergio Espeja"
|
14
|
+
EMAIL = "sergio.espeja@gmail.com"
|
15
|
+
DESCRIPTION = "bn4r is a bayesian networks library on ruby that provides the user with classes for create bayesian networks and diverse algorithms for solve them."
|
16
|
+
RUBYFORGE_PROJECT = "bn4r"
|
17
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
18
|
+
BIN_FILES = %w( )
|
19
|
+
|
20
|
+
|
21
|
+
NAME = "bn4r"
|
22
|
+
REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
23
|
+
VERS = ENV['VERSION'] || (Bn4r::VERSION::STRING + (REV ? ".#{REV}" : ""))
|
24
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
25
|
+
RDOC_OPTS = ['--quiet', '--title', "bn4r documentation",
|
26
|
+
"--opname", "index.html",
|
27
|
+
"--line-numbers",
|
28
|
+
"--main", "README",
|
29
|
+
"--inline-source"]
|
30
|
+
|
31
|
+
desc "Packages up bn4r gem."
|
32
|
+
task :default => [:test]
|
33
|
+
task :package => [:clean]
|
34
|
+
|
35
|
+
Rake::TestTask.new("test") { |t|
|
36
|
+
t.libs << "test"
|
37
|
+
t.pattern = "test/**/*_test.rb"
|
38
|
+
t.verbose = true
|
39
|
+
}
|
40
|
+
|
41
|
+
spec =
|
42
|
+
Gem::Specification.new do |s|
|
43
|
+
s.name = NAME
|
44
|
+
s.version = VERS
|
45
|
+
s.platform = Gem::Platform::RUBY
|
46
|
+
s.has_rdoc = true
|
47
|
+
s.extra_rdoc_files = ["README", "CHANGELOG"]
|
48
|
+
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
|
49
|
+
s.summary = DESCRIPTION
|
50
|
+
s.description = DESCRIPTION
|
51
|
+
s.author = AUTHOR
|
52
|
+
s.email = EMAIL
|
53
|
+
s.homepage = HOMEPATH
|
54
|
+
s.executables = BIN_FILES
|
55
|
+
s.rubyforge_project = RUBYFORGE_PROJECT
|
56
|
+
s.bindir = "bin"
|
57
|
+
s.require_path = "lib"
|
58
|
+
s.autorequire = "bn4r"
|
59
|
+
|
60
|
+
s.add_dependency('rgl', '>=0.2.3')
|
61
|
+
#s.required_ruby_version = '>= 1.8.2'
|
62
|
+
|
63
|
+
s.files = %w(README CHANGELOG Rakefile) +
|
64
|
+
Dir.glob("{bin,doc,test,lib,templates,generator,extras,website,script}/**/*") +
|
65
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
66
|
+
Dir.glob("examples/**/*.rb") +
|
67
|
+
Dir.glob("tools/*.rb")
|
68
|
+
|
69
|
+
# s.extensions = FileList["ext/**/extconf.rb"].to_a
|
70
|
+
end
|
71
|
+
|
72
|
+
Rake::GemPackageTask.new(spec) do |p|
|
73
|
+
p.need_tar = true
|
74
|
+
p.gem_spec = spec
|
75
|
+
end
|
76
|
+
|
77
|
+
task :install do
|
78
|
+
name = "#{NAME}-#{VERS}.gem"
|
79
|
+
sh %{rake package}
|
80
|
+
sh %{sudo gem install pkg/#{name}}
|
81
|
+
end
|
82
|
+
|
83
|
+
task :uninstall => [:clean] do
|
84
|
+
sh %{sudo gem uninstall #{NAME}}
|
85
|
+
end
|
data/lib/bn4r/bn.rb
ADDED
@@ -0,0 +1,249 @@
|
|
1
|
+
##############################################################
|
2
|
+
#
|
3
|
+
# Bayesian Network Library for Ruby
|
4
|
+
#
|
5
|
+
# Author: Sergio Espeja ( http://www.upf.edu/pdi/iula/sergio.espeja, sergio.espeja at gmail.com )
|
6
|
+
#
|
7
|
+
# Developed in: IULA ( http://www.iula.upf.es ) and
|
8
|
+
# in bee.com.es ( http://bee.com.es )
|
9
|
+
#
|
10
|
+
# Based on work by Ryan Dahl in
|
11
|
+
# http://www.math.rochester.edu/people/grads/rld/bayesnets
|
12
|
+
#
|
13
|
+
##############################################################
|
14
|
+
require 'rgl/adjacency'
|
15
|
+
require 'rgl/dot'
|
16
|
+
|
17
|
+
include RGL
|
18
|
+
|
19
|
+
class BayesNet < DirectedAdjacencyGraph
|
20
|
+
|
21
|
+
# DirectedAdjacencyGraph redefined methods
|
22
|
+
# ----------------------------------------
|
23
|
+
|
24
|
+
# Adds a directed edge between parent and child BayesNetNodes labeled
|
25
|
+
# with tag ( if tag is included, othewise the label is nil ).
|
26
|
+
def add_edge(parent, child, tag=nil)
|
27
|
+
raise "Nodes must be of the class BayesNetNodes" if parent.class != BayesNetNode or child.class != BayesNetNode
|
28
|
+
edge = super(parent, child)
|
29
|
+
child.parents << parent
|
30
|
+
child.relations << tag if !tag.nil?
|
31
|
+
edge
|
32
|
+
end
|
33
|
+
|
34
|
+
# BN Methods
|
35
|
+
# ----------
|
36
|
+
|
37
|
+
# Clears the value of all BayesNetNodes in Bayes Net.
|
38
|
+
def clear_values!
|
39
|
+
vertices.each { |v| v.clear_value }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Gets the variable with given name.
|
43
|
+
def get_variable( text )
|
44
|
+
vertices.each { |v| return v if v.name == text }
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns the root nodes of the Bayes Net.
|
48
|
+
def roots
|
49
|
+
vertices.select { |v| root?(v) }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns true/false if given Node is root.
|
53
|
+
def root?(v)
|
54
|
+
return true if num_parents(v) == 0
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns the number of parents of a node.
|
59
|
+
def num_parents(v)
|
60
|
+
return v.parents.size
|
61
|
+
end
|
62
|
+
|
63
|
+
# Return the probability of a distribution in the bayes net
|
64
|
+
# all nodes in the Bayes Net must have a value, otherwise
|
65
|
+
# will raise a exception
|
66
|
+
def inference_by_enumeration
|
67
|
+
prob = 1.0;
|
68
|
+
vertices.each {|v| prob = prob * p_v_cond_parents(v)}
|
69
|
+
prob
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns the probability of a node conditioned to his parents:
|
73
|
+
# P(v|parents(v))
|
74
|
+
def p_v_cond_parents(v)
|
75
|
+
givens_assignments = v.parents.collect {|parents| parents.value}
|
76
|
+
v.get_probability(v.value, givens_assignments).to_f
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns true if all nodes have values.
|
80
|
+
def all_nodes_with_values?
|
81
|
+
vertices.select {|v| !v.value.nil? }.size == vertices.size
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns nodes ordered by dependencies ( from those who haven't ( roots )
|
85
|
+
# to leaves ).
|
86
|
+
def nodes_ordered_by_dependencies(nodes = vertices, bn_ordered = Array.new)
|
87
|
+
nodes.each { |v|
|
88
|
+
next if bn_ordered.include?(v)
|
89
|
+
nodes_ordered_by_dependencies(v.parents, bn_ordered) if !v.root?
|
90
|
+
bn_ordered << v
|
91
|
+
}
|
92
|
+
return bn_ordered.flatten
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
class BayesNetNode
|
99
|
+
attr_reader :name, :outcomes, :extra_info, :value, :relations
|
100
|
+
|
101
|
+
# create de BayesNetNode
|
102
|
+
# name is the identifier of the node in the Bayesian Network
|
103
|
+
# outcomes is an array with the posible values that this node
|
104
|
+
# can have, by default [true, false].
|
105
|
+
# [optional] extra_info extra information that can be putted in the node
|
106
|
+
def initialize ( name , outcomes = [true, false], extra_info = nil)
|
107
|
+
@name = name
|
108
|
+
@outcomes = outcomes
|
109
|
+
@extra_info = extra_info
|
110
|
+
@givens = []
|
111
|
+
@relations = []
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns a copy of the node itself
|
115
|
+
def copy
|
116
|
+
tmp = BayesNetNode.new(@name, @outcomes, @extra_info)
|
117
|
+
tmp.set_value(@value)
|
118
|
+
tmp.set_probability_table(@givens, @table)
|
119
|
+
tmp
|
120
|
+
end
|
121
|
+
|
122
|
+
def set_value(value)
|
123
|
+
@value = value
|
124
|
+
end
|
125
|
+
|
126
|
+
def clear_value
|
127
|
+
@value = nil
|
128
|
+
end
|
129
|
+
|
130
|
+
# Return node parents
|
131
|
+
def parents
|
132
|
+
return @givens
|
133
|
+
end
|
134
|
+
|
135
|
+
# Return node relations
|
136
|
+
def relations
|
137
|
+
return @relations
|
138
|
+
end
|
139
|
+
|
140
|
+
# Return the number of parents
|
141
|
+
def num_parents
|
142
|
+
parents.size
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns true if the node is a root node ( doesn't have parents ).
|
146
|
+
def root?
|
147
|
+
return true if num_parents == 0
|
148
|
+
false
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns true if all parents of the node in the bn have values
|
152
|
+
def all_parents_with_values?
|
153
|
+
parents.select {|v| !v.value.nil? }.size == parents.size
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_s
|
157
|
+
return name + (value.nil? ? "" : (" = " + value.to_s))
|
158
|
+
end
|
159
|
+
|
160
|
+
# if givens is nil, then internal givens is assumed
|
161
|
+
# table must have probability values in order like BAD!!!
|
162
|
+
# [g0=pos0 & g1=pos0 & ... & node_value=pos0, ..., g0=pos0 & g1=pos0 & ... & node_value=posN,
|
163
|
+
#
|
164
|
+
def set_probability_table (givens, table)
|
165
|
+
# perhaps we should do some error checking on the table entries here?
|
166
|
+
@table_is_a_proc = (table.class != Array)
|
167
|
+
@givens = givens if !givens.nil?
|
168
|
+
|
169
|
+
raise "Error table incorrect number of positions (" \
|
170
|
+
+ table.size.to_s + " of " + self.get_table_size.to_s \
|
171
|
+
+ ")" if table.size != self.get_table_size
|
172
|
+
|
173
|
+
@table = table
|
174
|
+
end
|
175
|
+
|
176
|
+
def parents_assignments
|
177
|
+
parents.collect { |p| p.value }
|
178
|
+
end
|
179
|
+
|
180
|
+
# returns the number of cells that conditional probability table ( CPT )
|
181
|
+
# haves.
|
182
|
+
def get_table_size
|
183
|
+
num = @outcomes.size
|
184
|
+
@givens.each { |given| num = num * given.outcomes.size }
|
185
|
+
return num
|
186
|
+
end
|
187
|
+
|
188
|
+
# Sets a probability to the node with a node_assignment conditioned to given_assignments
|
189
|
+
# P (node = node_assignment | givens = givens_assignments) = number
|
190
|
+
def set_probability(number, node_assignment, givens_assignments)
|
191
|
+
@table[get_table_index(node_assignment, givens_assignments)] = number
|
192
|
+
end
|
193
|
+
|
194
|
+
# Returns the probability of an assignment to a node conditioned to given_assignments
|
195
|
+
# P(node = node_assignment | givens = givens_assignments)
|
196
|
+
#
|
197
|
+
# All givens_assigments must have value.
|
198
|
+
def get_probability(node_assignment = value, givens_assignments = parents_assignments)
|
199
|
+
# raise "Node must have a value and a givens_assignments, otherwise put" \
|
200
|
+
# + "them in function call" if node_assignment.nil? or givens_assignments.nil?
|
201
|
+
# if there's a cached table take the index
|
202
|
+
return @table[get_table_index(node_assignment, givens_assignments)] if @table_is_a_proc.nil? or !@table_is_a_proc
|
203
|
+
# if the value is calculated on the fly using a function instead of
|
204
|
+
# a table
|
205
|
+
return @table[node_assignment, givens_assignments]
|
206
|
+
end
|
207
|
+
|
208
|
+
# Returns the corresponding index for the probability table given
|
209
|
+
# <i>node_assignment</i> and <i>givens_assignments</i>
|
210
|
+
def get_table_index(node_assignment, givens_assignments)
|
211
|
+
x = []
|
212
|
+
indices = []
|
213
|
+
index = 0
|
214
|
+
|
215
|
+
if givens_assignments.length != @givens.length
|
216
|
+
raise "Error. Number of assignments does not match node."
|
217
|
+
end
|
218
|
+
|
219
|
+
if @givens.length > 0
|
220
|
+
# create a indices array with the position of each value of
|
221
|
+
# given assignments.
|
222
|
+
givens_assignments.length.times { |i|
|
223
|
+
assignment = givens_assignments[i]
|
224
|
+
indices[i] = @givens[i].outcomes.index(assignment)
|
225
|
+
}
|
226
|
+
|
227
|
+
# create a array with the number of possible values each
|
228
|
+
# given node and this node itself can have ( node.outcomes.length )
|
229
|
+
# plus all next nodes.
|
230
|
+
x[givens_assignments.length-1] = @outcomes.length
|
231
|
+
(givens_assignments.length-2).downto(0) { |j|
|
232
|
+
x[j] = x[j+1] * @givens[j+1].outcomes.length
|
233
|
+
}
|
234
|
+
|
235
|
+
# to get the index, sum for each assignment the
|
236
|
+
# product of each given assignment outcomes size
|
237
|
+
# by its value index.
|
238
|
+
givens_assignments.length.times { |i|
|
239
|
+
index += x[i] * indices[i]
|
240
|
+
}
|
241
|
+
end
|
242
|
+
|
243
|
+
index += @outcomes.index(node_assignment)
|
244
|
+
|
245
|
+
return index
|
246
|
+
end
|
247
|
+
|
248
|
+
|
249
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
##############################################################
|
2
|
+
#
|
3
|
+
# Inference Algorithms for Bayesian Network Library for Ruby
|
4
|
+
#
|
5
|
+
# Author: Sergio Espeja ( http://www.upf.edu/pdi/iula/sergio.espeja, sergio.espeja at gmail.com )
|
6
|
+
#
|
7
|
+
# Developed in: IULA ( http://www.iula.upf.es ) and
|
8
|
+
# in bee.com.es ( http://bee.com.es )
|
9
|
+
#
|
10
|
+
# == Current implemented algorithms
|
11
|
+
# * enumeration_ask
|
12
|
+
# * prior_sample
|
13
|
+
# * rejection_sampling
|
14
|
+
# * likelihood_weighting
|
15
|
+
#
|
16
|
+
##############################################################
|
17
|
+
class BayesNet < DirectedAdjacencyGraph
|
18
|
+
|
19
|
+
# Inference Algorithms
|
20
|
+
|
21
|
+
# ENUMERATION ASK algorithm
|
22
|
+
#
|
23
|
+
# Implementation based on: S.Russell, P.Norving, "Artificial
|
24
|
+
# Intelligence, A Modern Approach", 2nd Edition. pp 506
|
25
|
+
#
|
26
|
+
# <b>x</b> --> query variable
|
27
|
+
#
|
28
|
+
# <b>e</b> --> variables with observed values
|
29
|
+
def enumeration_ask(x,e, bn_vertices = vertices)
|
30
|
+
e << x
|
31
|
+
q = []
|
32
|
+
#p bn_vertices.collect { |v| v.name }
|
33
|
+
x.outcomes.each {|outcome|
|
34
|
+
x.set_value(outcome)
|
35
|
+
q << enumerate_all(bn_vertices, e)
|
36
|
+
}
|
37
|
+
q
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns a sample from prior joint distribution specified by the network.
|
41
|
+
#
|
42
|
+
# Implementation based on: S.Russell, P.Norving, "Artificial
|
43
|
+
# Intelligence, A Modern Approach", 2nd Edition. pp 511-512
|
44
|
+
#
|
45
|
+
# The input are the nodes of the bn ordered by dependencies see nodes_ordered_by_dependencies
|
46
|
+
def prior_sample(nodes_ordered = nodes_ordered_by_dependencies)
|
47
|
+
sample = Array.new
|
48
|
+
nodes_ordered.each { |v|
|
49
|
+
value = rand < v.get_probability(true)
|
50
|
+
v.set_value(value)
|
51
|
+
sample << v.copy
|
52
|
+
}
|
53
|
+
# leave the bn clear of values.
|
54
|
+
nodes_ordered.each { |v| v.clear_value }
|
55
|
+
|
56
|
+
return sample
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns an estimation of P(X=x|e) = <P(X=x|e), 1 - P(X=x|e)> obtained. Generates samples from prior joint
|
60
|
+
# distribution specified by the network, rejects all those that do not match the evidence,
|
61
|
+
# and finally counts hoy often X = x occurs in remaining samples.
|
62
|
+
#
|
63
|
+
# Caution, this algorthm is unusable for complex problems because rejects many samples!
|
64
|
+
#
|
65
|
+
# Implementation based on: S.Russell, P.Norving, "Artificial
|
66
|
+
# Intelligence, A Modern Approach", 2nd Edition. pp 513
|
67
|
+
#
|
68
|
+
# <b>x</b> --> query variable
|
69
|
+
#
|
70
|
+
# <b>e</b> --> variables with observed values
|
71
|
+
#
|
72
|
+
# <b>n</b> --> Number of samples generated
|
73
|
+
#
|
74
|
+
def rejection_sampling( x, e, n, bn = self )
|
75
|
+
|
76
|
+
evidece_list = [e] if e.class != Array
|
77
|
+
x_list = [x] if x.class != Array
|
78
|
+
|
79
|
+
nodes_ordered = bn.nodes_ordered_by_dependencies
|
80
|
+
evidence_vector = get_vector_value(evidece_list, nodes_ordered)
|
81
|
+
x_vector = get_vector_value(x_list, nodes_ordered)
|
82
|
+
|
83
|
+
total_valid = 0; total_correct = 0
|
84
|
+
n.times do
|
85
|
+
sample_vector = bn.prior_sample(nodes_ordered).collect {|v| v.value}
|
86
|
+
|
87
|
+
valid = true; correct = true
|
88
|
+
for i in 0..(sample_vector.size-1) do
|
89
|
+
correct = false if !x_vector[i].nil? and sample_vector[i] != x_vector[i]
|
90
|
+
valid = false and break if !evidence_vector[i].nil? and sample_vector[i] != evidence_vector[i]
|
91
|
+
end
|
92
|
+
|
93
|
+
next if !valid
|
94
|
+
total_valid += 1
|
95
|
+
total_correct += 1 if correct
|
96
|
+
end
|
97
|
+
|
98
|
+
p_true = total_correct.to_f/total_valid.to_f
|
99
|
+
return [p_true, 1-p_true]
|
100
|
+
#return [total_correct.to_f, total_valid.to_f]
|
101
|
+
end
|
102
|
+
|
103
|
+
# Returns an estimation of P(X=x|e) = <P(X=x|e), 1 - P(X=x|e)> obtained.
|
104
|
+
#
|
105
|
+
# Implementation based on: S.Russell, P.Norving, "Artificial
|
106
|
+
# Intelligence, A Modern Approach", 2nd Edition. pp 515
|
107
|
+
#
|
108
|
+
# <b>x</b> --> query variable
|
109
|
+
#
|
110
|
+
# <b>e</b> --> variables with observed values
|
111
|
+
#
|
112
|
+
# <b>n</b> --> Number of samples generated
|
113
|
+
#
|
114
|
+
def likelihood_weighting( x, e, n, bn = self )
|
115
|
+
|
116
|
+
retval = [0.0, 0.0]
|
117
|
+
n.times {
|
118
|
+
w_sample, w = weighted_sample(e)
|
119
|
+
value = w_sample.select { |v| v.name == x.name }[0].value
|
120
|
+
#p value
|
121
|
+
if value == x.value
|
122
|
+
retval[1] += w
|
123
|
+
else
|
124
|
+
retval[0] += w
|
125
|
+
end
|
126
|
+
}
|
127
|
+
|
128
|
+
norm = retval[1].to_f / (retval[0]+retval[1]).to_f
|
129
|
+
|
130
|
+
return [norm, 1-norm]
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
# Auxiliar function to compute Enumeration Ask Algorithm
|
135
|
+
def enumerate_all(vars, e)
|
136
|
+
|
137
|
+
return 1.0 if vars.empty?
|
138
|
+
|
139
|
+
y = vars.first; i = 1
|
140
|
+
while !y.all_parents_with_values? and i < vars.size
|
141
|
+
y = vars[i]
|
142
|
+
i = i + 1
|
143
|
+
end
|
144
|
+
raise "Error bayes net not computable with enumeration-ask " + \
|
145
|
+
"algorithm" if i == vars.size and !y.all_parents_with_values?
|
146
|
+
|
147
|
+
if e.include?(y)
|
148
|
+
return p_v_cond_parents(y) * enumerate_all(vars-[y], e)
|
149
|
+
else
|
150
|
+
prob = 0.0
|
151
|
+
y.outcomes.each { |outcome|
|
152
|
+
y.set_value(outcome)
|
153
|
+
prob = prob + p_v_cond_parents(y) * enumerate_all(vars-[y], e+[y])
|
154
|
+
y.clear_value
|
155
|
+
}
|
156
|
+
return prob
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Returns an event and a weight.
|
161
|
+
#
|
162
|
+
# Implementation based on: S.Russell, P.Norving, "Artificial
|
163
|
+
# Intelligence, A Modern Approach", 2nd Edition. pp 515
|
164
|
+
#
|
165
|
+
# <b>e</b> --> variables with observed values
|
166
|
+
#
|
167
|
+
def weighted_sample(e, bn = self)
|
168
|
+
|
169
|
+
nodes_ordered = bn.nodes_ordered_by_dependencies
|
170
|
+
|
171
|
+
sample = Array.new
|
172
|
+
w = 1.0
|
173
|
+
nodes_ordered.each { |v|
|
174
|
+
node_actual = e.select { |node| node.name == v.name } if e.class == Array
|
175
|
+
node_actual = [e] if e.class == BayesNetNode and e.name == v.name
|
176
|
+
if !node_actual.nil? and node_actual.size == 1
|
177
|
+
value = node_actual[0].value
|
178
|
+
w = w * v.get_probability(value)
|
179
|
+
else
|
180
|
+
value = rand < v.get_probability(true)
|
181
|
+
end
|
182
|
+
v.set_value(value)
|
183
|
+
sample << v.copy
|
184
|
+
}
|
185
|
+
|
186
|
+
# leave the bn clear of values.
|
187
|
+
bn.clear_values!
|
188
|
+
return sample, w
|
189
|
+
end
|
190
|
+
|
191
|
+
# Axiliar function that returns an array of bn_vertices_ordered.size positions
|
192
|
+
# with nil if position aren't in vertices_vector, and value if there's a match
|
193
|
+
# in vertices_vector.
|
194
|
+
def get_vector_value(vertices_vector, bn_vertices_ordered)
|
195
|
+
bn_vertices_ordered.collect { |v|
|
196
|
+
if !vertices_vector.nil?
|
197
|
+
node_actual = vertices_vector.select { |node| node.name == v.name }
|
198
|
+
|
199
|
+
case node_actual.size
|
200
|
+
when 0
|
201
|
+
nil
|
202
|
+
when 1
|
203
|
+
node_actual[0].value
|
204
|
+
else
|
205
|
+
raise "Error in get_vector_value"
|
206
|
+
end
|
207
|
+
else
|
208
|
+
nil
|
209
|
+
end
|
210
|
+
}
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
|
215
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
##############################################################
|
2
|
+
#
|
3
|
+
# Methods for poulating Bayes Net Probabilities tables.
|
4
|
+
#
|
5
|
+
# Author: Sergio Espeja ( http://www.upf.edu/pdi/iula/sergio.espeja, sergio.espeja at gmail.com )
|
6
|
+
#
|
7
|
+
# Developed in: IULA ( http://www.iula.upf.es )
|
8
|
+
#
|
9
|
+
##############################################################
|
10
|
+
|
11
|
+
|
12
|
+
# Bayes Net Table Probabilities Generator
|
13
|
+
class BnTableProbabilitiesGenerator
|
14
|
+
def get_node_probability_from_boolean_combination(boolean_combination)
|
15
|
+
0.0
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Bayes Net Table Probabilities Generator from Positive Negative Relations
|
20
|
+
class BNTPGFromPositiveNegativeRelations < BnTableProbabilitiesGenerator
|
21
|
+
|
22
|
+
public
|
23
|
+
# type_of_position_impact is a array of boolean values showing the
|
24
|
+
# relation ( positive | negative ) beetwen the node and its parents.
|
25
|
+
def table_probabilities_for_node(node, type_of_position_impact)
|
26
|
+
raise "Node parents and type_of_position_impact with different size" if node.parents.size != type_of_position_impact.size
|
27
|
+
boolean_combinations = []
|
28
|
+
(2**node.parents.size).times { |i|
|
29
|
+
boolean_combination = Array.new(node.parents.size, false)
|
30
|
+
actual_value = i
|
31
|
+
(node.parents.size).times { |j|
|
32
|
+
boolean_combination[j] = !(actual_value%2 == 0)
|
33
|
+
actual_value = actual_value / 2
|
34
|
+
}
|
35
|
+
boolean_combinations << boolean_combination
|
36
|
+
}
|
37
|
+
#p boolean_combinations
|
38
|
+
table_probabilities = [] # Array.new(2**(node.parents.size+1))
|
39
|
+
boolean_combinations.each { |boolean_combination|
|
40
|
+
[true,false].each { |node_value|
|
41
|
+
prob = BNTPGFromPositiveNegativeRelations.get_node_probability_from_boolean_combination(boolean_combination, type_of_position_impact)
|
42
|
+
prob = 1 - prob if node_value == false
|
43
|
+
table_probabilities[node.get_table_index(node_value, boolean_combination)] = prob
|
44
|
+
#p "pos :" + node.get_table_index(node_value, boolean_combination).to_s
|
45
|
+
#p "Ok :" + node.get_table_index(node_value, boolean_combination).to_s if node.get_table_index(node_value, boolean_combination) > 2**node.parents.size
|
46
|
+
}
|
47
|
+
}
|
48
|
+
#p table_probabilities
|
49
|
+
table_probabilities
|
50
|
+
end
|
51
|
+
|
52
|
+
# returns P(node=yes| parents={boolean_combination}) where parents have
|
53
|
+
# relations with the node showed in type_of_position_impact
|
54
|
+
# type_of_position_impact is a array of boolean values showing the
|
55
|
+
# relation ( positive | negative ) beetwen the node and its parents.
|
56
|
+
def self.get_node_probability_from_boolean_combination(boolean_combination, type_of_position_impact)
|
57
|
+
num_eq = 0.0
|
58
|
+
boolean_combination.size.times { |i|
|
59
|
+
num_eq = num_eq + 1.0 if type_of_position_impact[i] && boolean_combination[i]
|
60
|
+
num_eq = num_eq + 1.0 if !type_of_position_impact[i] && !boolean_combination[i]
|
61
|
+
}
|
62
|
+
num_eq / boolean_combination.size.to_f
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/lib/bn4r/version.rb
ADDED
data/lib/bn4r.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
##############################################################
|
2
|
+
#
|
3
|
+
# Bayesian Network Library for Ruby
|
4
|
+
#
|
5
|
+
# Author: Sergio Espeja ( sergio.espeja@gmail.com )
|
6
|
+
#
|
7
|
+
# Developed in: IULA ( http://www.iula.upf.es ) and
|
8
|
+
# in bee.com.es ( http://bee.com.es )
|
9
|
+
#
|
10
|
+
##############################################################
|
11
|
+
Dir[File.join(File.dirname(__FILE__), 'bn4r/**/*.rb')].sort.each { |lib| require lib }
|
data/test/bn4r_test.rb
ADDED
@@ -0,0 +1,290 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper.rb'
|
2
|
+
|
3
|
+
class Bn4rTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
# Returns the BayesNet used as example in "Artificial Intelligence A
|
6
|
+
# Modern Approach, Rusell & Norvig, 2nd Ed." pp.494
|
7
|
+
def self.bayes_net_aima
|
8
|
+
bn_aima = BayesNet.new
|
9
|
+
|
10
|
+
b = BayesNetNode.new("Burglary")
|
11
|
+
e = BayesNetNode.new("Earthquake")
|
12
|
+
a = BayesNetNode.new("Alarm")
|
13
|
+
j = BayesNetNode.new("JohnCalls")
|
14
|
+
m = BayesNetNode.new("MaryCalls")
|
15
|
+
|
16
|
+
bn_aima.add_vertex(b)
|
17
|
+
bn_aima.add_vertex(e)
|
18
|
+
bn_aima.add_vertex(a)
|
19
|
+
bn_aima.add_vertex(j)
|
20
|
+
bn_aima.add_vertex(m)
|
21
|
+
|
22
|
+
bn_aima.add_edge(b,a)
|
23
|
+
bn_aima.add_edge(e,a)
|
24
|
+
bn_aima.add_edge(a,j)
|
25
|
+
bn_aima.add_edge(a,m)
|
26
|
+
|
27
|
+
b.set_probability_table([], [0.001, 0.999] )
|
28
|
+
e.set_probability_table([], [0.002, 0.998] )
|
29
|
+
|
30
|
+
a.set_probability_table([b,e], [0.95, 0.05, 0.94, 0.06, 0.29, 0.61, 0.001,0.999] )
|
31
|
+
|
32
|
+
j.set_probability_table([a], [0.90,0.10,0.05,0.95])
|
33
|
+
m.set_probability_table([a], [0.70,0.30,0.01,0.99])
|
34
|
+
|
35
|
+
bn_aima
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns the BayesNet used as example in "Artificial Intelligence A
|
39
|
+
# Modern Approach, Rusell & Norvig, 2nd Ed." pp.510
|
40
|
+
def self.bayes_net_aima2
|
41
|
+
bn_aima = BayesNet.new
|
42
|
+
|
43
|
+
cloudy = BayesNetNode.new("Cloudy")
|
44
|
+
sprinkler = BayesNetNode.new("Sprinkler")
|
45
|
+
rain = BayesNetNode.new("Rain")
|
46
|
+
wetgrass = BayesNetNode.new("WetGrass")
|
47
|
+
|
48
|
+
bn_aima.add_vertex(cloudy)
|
49
|
+
bn_aima.add_vertex(sprinkler)
|
50
|
+
bn_aima.add_vertex(rain)
|
51
|
+
bn_aima.add_vertex(wetgrass)
|
52
|
+
|
53
|
+
bn_aima.add_edge(cloudy,sprinkler)
|
54
|
+
bn_aima.add_edge(cloudy,rain)
|
55
|
+
bn_aima.add_edge(sprinkler,wetgrass)
|
56
|
+
bn_aima.add_edge(rain,wetgrass)
|
57
|
+
|
58
|
+
cloudy.set_probability_table([], [0.5, 0.5] )
|
59
|
+
|
60
|
+
sprinkler.set_probability_table([cloudy], [0.1, 0.9, 0.5, 0.5] )
|
61
|
+
rain.set_probability_table([cloudy], [0.8, 0.2, 0.2, 0.8] )
|
62
|
+
|
63
|
+
wetgrass.set_probability_table([sprinkler, rain], [0.99, 0.01, 0.9, 0.1, 0.9, 0.1, 0.0, 1.0] )
|
64
|
+
bn_aima
|
65
|
+
end
|
66
|
+
|
67
|
+
def bayes_net_aaile
|
68
|
+
bn = BayesNet.new
|
69
|
+
rel = BayesNetNode.new("relational")
|
70
|
+
q = BayesNetNode.new("qualificative")
|
71
|
+
a = BayesNetNode.new("Adverbial")
|
72
|
+
bn.add_vertex(rel)
|
73
|
+
bn.add_vertex(q)
|
74
|
+
bn.add_vertex(a)
|
75
|
+
|
76
|
+
# ["preN", ...]
|
77
|
+
preN = BayesNetNode.new("preN")
|
78
|
+
bn.add_vertex(preN)
|
79
|
+
|
80
|
+
postN = BayesNetNode.new("postN")
|
81
|
+
bn.add_vertex(postN)
|
82
|
+
|
83
|
+
# bn.add_edge("ser")
|
84
|
+
# bn.add_edge("G")
|
85
|
+
# bn.add_edge("prep")
|
86
|
+
|
87
|
+
bn.add_edge(rel, preN)
|
88
|
+
bn.add_edge(q, preN)
|
89
|
+
bn.add_edge(a, preN)
|
90
|
+
bn.add_edge(q, postN)
|
91
|
+
|
92
|
+
bn
|
93
|
+
end
|
94
|
+
|
95
|
+
def setup
|
96
|
+
end
|
97
|
+
|
98
|
+
# TESTS
|
99
|
+
|
100
|
+
def test_create_sample_bn
|
101
|
+
bn = bayes_net_aaile
|
102
|
+
|
103
|
+
assert_equal bn.vertices.size, 5
|
104
|
+
assert_equal bn.edges.size, 4
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def test_graph_viz
|
109
|
+
bn = bayes_net_aaile
|
110
|
+
# print "\n\n" + bn.to_dot_graph.to_s
|
111
|
+
|
112
|
+
assert bn.to_dot_graph.to_s.size > 0, "bn.to_dot_graph gives no output."
|
113
|
+
end
|
114
|
+
|
115
|
+
def test_probability_assingment
|
116
|
+
bn_aima = Bn4rTest.bayes_net_aima
|
117
|
+
b = bn_aima.get_variable("Burglary")
|
118
|
+
a = bn_aima.get_variable("Alarm")
|
119
|
+
j = bn_aima.get_variable("JohnCalls")
|
120
|
+
m = bn_aima.get_variable("MaryCalls")
|
121
|
+
|
122
|
+
assert_equal b.get_probability(true, []), 0.001
|
123
|
+
assert_equal b.get_probability(false, []), 0.999
|
124
|
+
assert_equal b.get_probability(false, []), 0.999
|
125
|
+
|
126
|
+
assert_equal a.get_probability(true, [false,false]), 0.001
|
127
|
+
assert_equal a.get_probability(true, [true,false]), 0.94
|
128
|
+
assert_equal a.get_probability(false, [true,true]), 0.05
|
129
|
+
|
130
|
+
assert_equal j.get_probability(true, [true]), 0.90
|
131
|
+
assert_equal m.get_probability(true, [false]), 0.01
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_CPT_size
|
136
|
+
bn_aima = Bn4rTest.bayes_net_aima
|
137
|
+
b = bn_aima.get_variable("Burglary")
|
138
|
+
a = bn_aima.get_variable("Alarm")
|
139
|
+
j = bn_aima.get_variable("JohnCalls")
|
140
|
+
m = bn_aima.get_variable("MaryCalls")
|
141
|
+
|
142
|
+
assert_equal b.get_table_size, 2
|
143
|
+
assert_equal a.get_table_size, 8
|
144
|
+
assert_equal j.get_table_size, 4
|
145
|
+
assert_equal m.get_table_size, 4
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_base_methods
|
149
|
+
bn_aima = Bn4rTest.bayes_net_aima
|
150
|
+
b = bn_aima.get_variable("Burglary")
|
151
|
+
a = bn_aima.get_variable("Alarm")
|
152
|
+
assert bn_aima.root?(b)
|
153
|
+
assert !bn_aima.root?(a)
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
def test_inference_by_enumeration
|
158
|
+
bn_aima = Bn4rTest.bayes_net_aima
|
159
|
+
b = bn_aima.get_variable("Burglary")
|
160
|
+
e = bn_aima.get_variable("Earthquake")
|
161
|
+
a = bn_aima.get_variable("Alarm")
|
162
|
+
j = bn_aima.get_variable("JohnCalls")
|
163
|
+
m = bn_aima.get_variable("MaryCalls")
|
164
|
+
|
165
|
+
assert_equal bn_aima.vertices.size, 5
|
166
|
+
assert_equal bn_aima.edges.size, 4
|
167
|
+
|
168
|
+
assert !bn_aima.all_nodes_with_values?
|
169
|
+
b.set_value(false); assert !bn_aima.all_nodes_with_values?
|
170
|
+
e.set_value(false); assert !bn_aima.all_nodes_with_values?
|
171
|
+
a.set_value(true); assert !bn_aima.all_nodes_with_values?
|
172
|
+
j.set_value(true); assert !bn_aima.all_nodes_with_values?
|
173
|
+
m.set_value(true)
|
174
|
+
|
175
|
+
assert bn_aima.all_nodes_with_values?
|
176
|
+
|
177
|
+
value = bn_aima.inference_by_enumeration
|
178
|
+
assert_in_delta 0.0006281112, value, 10**(-10)
|
179
|
+
|
180
|
+
bn_aima.clear_values!
|
181
|
+
assert !bn_aima.all_nodes_with_values?
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
def test_inference_by_enumeration_ask
|
186
|
+
bn_aima = Bn4rTest.bayes_net_aima
|
187
|
+
b = bn_aima.get_variable("Burglary")
|
188
|
+
e = bn_aima.get_variable("Earthquake")
|
189
|
+
a = bn_aima.get_variable("Alarm")
|
190
|
+
j = bn_aima.get_variable("JohnCalls")
|
191
|
+
m = bn_aima.get_variable("MaryCalls")
|
192
|
+
j.set_value(true)
|
193
|
+
m.set_value(true)
|
194
|
+
|
195
|
+
assert_equal bn_aima.vertices.size, 5
|
196
|
+
assert_equal bn_aima.edges.size, 4
|
197
|
+
value1 = bn_aima.enumeration_ask(b,[j,m])
|
198
|
+
value2 = bn_aima.enumeration_ask(b,[j,m], [j,m,a,b,e])
|
199
|
+
value3 = bn_aima.enumeration_ask(b,[j,m], [b,e,a,j,m])
|
200
|
+
assert_equal value1[0], value2[0]
|
201
|
+
assert_equal value1[0].to_f, value3[0].to_f
|
202
|
+
assert_in_delta value1[1].to_f, value2[1].to_f, 10**(-10)
|
203
|
+
assert_equal value1[1], value3[1]
|
204
|
+
assert_in_delta 0.000592242, bn_aima.enumeration_ask(b,[j,m])[0], 10**(-9)
|
205
|
+
bn_aima.clear_values!
|
206
|
+
assert !bn_aima.all_nodes_with_values?
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
|
211
|
+
def test_table_probabilities_for_node
|
212
|
+
bn_aima = Bn4rTest.bayes_net_aima
|
213
|
+
b = bn_aima.get_variable("Burglary")
|
214
|
+
e = bn_aima.get_variable("Earthquake")
|
215
|
+
a = bn_aima.get_variable("Alarm")
|
216
|
+
BNTPGFromPositiveNegativeRelations.new.table_probabilities_for_node(a, [true,true])
|
217
|
+
|
218
|
+
# BNTPGFromPositiveNegativeRelations.new.populate_bn_with_tags(bn_aima)
|
219
|
+
assert true
|
220
|
+
end
|
221
|
+
|
222
|
+
def test_prior_sampling
|
223
|
+
|
224
|
+
bn = Bn4rTest.bayes_net_aima2
|
225
|
+
|
226
|
+
hash = Hash.new(0)
|
227
|
+
nodes_ordered = bn.nodes_ordered_by_dependencies
|
228
|
+
10000.times { hash[ bn.prior_sample(nodes_ordered).collect {|v| v.value} ] += 1 }
|
229
|
+
|
230
|
+
combination_of_prob = nodes_ordered.collect { |v|
|
231
|
+
case v.name
|
232
|
+
when "Cloudy"
|
233
|
+
true
|
234
|
+
when "Sprinkler"
|
235
|
+
false
|
236
|
+
when "Rain"
|
237
|
+
true
|
238
|
+
when "WetGrass"
|
239
|
+
true
|
240
|
+
else
|
241
|
+
raise "Incorrect BayesNet created at Bn4rTest.bayes_net_aima2"
|
242
|
+
end
|
243
|
+
}
|
244
|
+
|
245
|
+
prob = hash[combination_of_prob].to_f/10000.0
|
246
|
+
|
247
|
+
assert_in_delta 0.3, prob, 0.1, "Its inprobable but possible that this error occurs, \
|
248
|
+
because we are working with statistics and random data, \
|
249
|
+
try again and if still occurs take care of it."
|
250
|
+
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_rejection_sampling
|
254
|
+
bn = Bn4rTest.bayes_net_aima2
|
255
|
+
|
256
|
+
rain = bn.get_variable("Rain").copy
|
257
|
+
sprinkler = bn.get_variable("Sprinkler").copy
|
258
|
+
|
259
|
+
rain.set_value(true)
|
260
|
+
sprinkler.set_value(true)
|
261
|
+
|
262
|
+
results = bn.rejection_sampling(rain, sprinkler, 1000)
|
263
|
+
|
264
|
+
str_error = " Its improbable but possible that this error occurs, \
|
265
|
+
because we are working with statistics and random data, \
|
266
|
+
try again and if still occurs take care of it."
|
267
|
+
assert ((results[0] > 0.2) and (results[0] < 0.4)), "Results aren't in interval [0.2,0.4] --> " + results[0].to_s + str_error
|
268
|
+
assert ((results[1] > 0.6) and (results[1] < 0.8)), "Results aren't in interval [0.6,0.8] --> " + results[1].to_s + str_error
|
269
|
+
end
|
270
|
+
|
271
|
+
def test_likelihood_weighting
|
272
|
+
bn = Bn4rTest.bayes_net_aima2
|
273
|
+
|
274
|
+
rain = bn.get_variable("Rain").copy
|
275
|
+
sprinkler = bn.get_variable("Sprinkler").copy
|
276
|
+
|
277
|
+
rain.set_value(true)
|
278
|
+
sprinkler.set_value(true)
|
279
|
+
|
280
|
+
results = bn.likelihood_weighting(rain, sprinkler, 100)
|
281
|
+
|
282
|
+
str_error = " Its improbable but possible that this error occurs, \
|
283
|
+
because we are working with statistics and random data, \
|
284
|
+
try again and if still occurs take care of it."
|
285
|
+
assert ((results[0] > 0.2) and (results[0] < 0.4)), "Results aren't in interval [0.2,0.4] --> " + results[0].to_s + str_error
|
286
|
+
assert ((results[1] > 0.6) and (results[1] < 0.8)), "Results aren't in interval [0.6,0.8] --> " + results[1].to_s + str_error
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: bn4r
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2006-10-31 00:00:00 +01:00
|
8
|
+
summary: bn4r is a bayesian networks library on ruby that provides the user with classes for create bayesian networks and diverse algorithms for solve them.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: sergio.espeja@gmail.com
|
12
|
+
homepage: http://bn4r.rubyforge.org
|
13
|
+
rubyforge_project: bn4r
|
14
|
+
description: bn4r is a bayesian networks library on ruby that provides the user with classes for create bayesian networks and diverse algorithms for solve them.
|
15
|
+
autorequire: bn4r
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
authors:
|
29
|
+
- Sergio Espeja
|
30
|
+
files:
|
31
|
+
- README
|
32
|
+
- CHANGELOG
|
33
|
+
- Rakefile
|
34
|
+
- test/test_helper.rb
|
35
|
+
- test/bn4r_test.rb
|
36
|
+
- lib/bn4r
|
37
|
+
- lib/bn4r.rb
|
38
|
+
- lib/bn4r/version.rb
|
39
|
+
- lib/bn4r/bn.rb
|
40
|
+
- lib/bn4r/bn_table_probabilities.rb
|
41
|
+
- lib/bn4r/bn_algorithms.rb
|
42
|
+
test_files: []
|
43
|
+
|
44
|
+
rdoc_options:
|
45
|
+
- --quiet
|
46
|
+
- --title
|
47
|
+
- bn4r documentation
|
48
|
+
- --opname
|
49
|
+
- index.html
|
50
|
+
- --line-numbers
|
51
|
+
- --main
|
52
|
+
- README
|
53
|
+
- --inline-source
|
54
|
+
- --exclude
|
55
|
+
- ^(examples|extras)/
|
56
|
+
extra_rdoc_files:
|
57
|
+
- README
|
58
|
+
- CHANGELOG
|
59
|
+
executables: []
|
60
|
+
|
61
|
+
extensions: []
|
62
|
+
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
dependencies:
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: rgl
|
68
|
+
version_requirement:
|
69
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 0.2.3
|
74
|
+
version:
|